home *** CD-ROM | disk | FTP | other *** search
Text File | 1985-09-30 | 46.2 KB | 1,223 lines |
- 1 dBASE III Anomalies and Workarounds
-
- 1.1 How to use this Section
-
-
- 1.2 @..SAY...GET to @...SAY...PICTURE
-
- >>> & Function
-
- The macro (&) function will not expand properly if it is followed by a
- space and parentheses. For example:
-
-
- STORE 'LIST FOR' TO x
- STORE 10 TO memvar
- &x Field1 = memvar
-
-
- will execute properly, but,
-
- &x (Field1 - memvar) * memvar = 0
-
- will return the "*** Unrecognized command verb" error message. This problem
- can be avoided by terminating the macro-substituted memory variable with a
- period. For example,
-
- &x. (Field1 - memvar) * memvar = 0
-
- will work. It is always a good idea to terminate a macro with a period.
-
-
-
-
- 1.3 APPEND to CONFIG.DB
-
- >>> CONFIG.DB with TEDIT or WP
-
- Config.db will not accept more than eight characters for WP or TEDIT. Any
- more than eight will be truncated. Attempting to use MODIFY COMMAND (or to
- edit a MEMO field) will briefly display the operating system message "Bad
- command or file name" and drop the user to the dBASE dot prompt (or to the
- edit screen). dBASE III warns the user that the filename is truncated when
- dBASE is initialized. For example:
-
- TEDIT=B:DFORMAT
- ^--- truncated
-
- To work around this problem, place the word processor in the same drive and
- directory or rename the wordprocessor with fewer characters.
-
-
-
- 1.4 COPY [STRUCTURE] to DO WHILE
-
- >>> DO WHILE with RESTORE
-
- If a memory variable tested in a DO WHILE loop is recreated in the loop by
- RESTOREing the variable FROM a memory file, the loop will continue to run
- even after the condition no longer evaluates as true (.T.). The program
- below will run endlessly as long as the control variable is not the first
- entry in the memory file:
-
- var = .T.
- DO WHILE var
- RESTORE FROM Memfile <--- This overwrites var at the
- same memory location.
- var = .F. <--- This change is ignored if
- ENDDO the previous assignment
- statement changed the
- memory location of the
- variable.
-
- RESTOREing ADDITIVE ameliorates the problem.
-
-
- >>> DO WHILE with semicolon
-
- When a DO WHILE conditional statement is continued to a second line with a
- semicolon, dBASE III tries to execute this second line the second time
- through the loop. When the ENDDO is encountered and the condition
- evaluates as true, program flow proceeds to this second line, resulting in
- the error message, "*** Unrecognized command verb." For example:
-
- * ---This program will give an error message
- * ---when "Y" is entered at the WAIT prompt.
- answer = 'Y'
- number = 1
- DO WHILE number < 10;
- .AND. answer = 'Y'
- ? number
- WAIT '"Y" to continue ' TO answer
- number = number + 1
- ENDDO
-
- If "Y" is entered at the WAIT prompt, dBASE III tries to execute ".AND.
- answer = 'Y'." However, if the semicolon is deleted and the line is allowed
- to wrap at column 67 in MODIFY COMMAND, execution flows correctly.
-
-
- >>> DO WHILE with RETURN
-
- A RETURN statement inside a DO WHILE...ENDDO construction will not close
- the DO WHILE <condition> in the program as it does in dBASE II. Therefore,
- the <condition> will continue to be evaluated. For example:
-
- * A.PRG
- expA = "1"
- DO WHILE expA = "1"
- ? "Hi!"
- DO B ---------------> * B.PRG
- ENDDO expB = "2"
- * EOF: A.PRG DO WHILE expB = "2"
- ? "How are you?"
- expA = "X"
- RETURN
- ENDDO
- * EOF: B.PRG
-
- Executing A.PRG will cause an infinite loop. It appears dBASE III
- continues to test the condition of expB. In order to work around this,
- B.PRG should be written as follows:
-
- * B.PRG (revised).
- expB = "2"
- DO WHILE expB = "2"
- ? "How are you?"
- expA = "X"
- EXIT <----- Notice, the EXIT here,
- ENDDO
- RETURN <----- and the RETURN here.
- * EOF: B.PRG
-
-
- >>> DO WHILE with an extra ENDDO
-
- If an extra ENDDO is added to a command file, an infinite loop will result.
- For example, the following program will execute until Esc is pressed:
-
- number = 0
- DO WHILE number < 10
- @ 10,10 SAY "Now at loop " + STR(number,2)
- STORE number + 1 TO number
- ENDDO
- ENDDO <--- This ENDDO has no matching DO WHILE.
- RETURN
-
- We recommend you use some method of indentation for the control structures:
- DO CASE...ENDCASE, DO WHILE...ENDDO, and IF...ENDIF to avoid this problem.
- This practice will make command files more readable, and will allow for
- quick visual checking for accuracy of nested control structures. In our
- example, two consecutive ENDDO statements with the same left margin is a
- definite indication that something is wrong.
-
-
-
- 1.5 EJECT to LABEL limitations
-
-
- 1.6 MODIFY STRUCTURE to PCOL() and SET MARGIN TO
-
- >>> Memo fields, listing or printing after a previous field
-
- A problem displaying memo fields has been found in dBASE III by one of our
- users. The problem occurs when you try to list or print a memo field
- preceded by a field whose length will force the first line of the memo
- field to wrap around one or more times.
-
- Assume a file structure consisting of two fields:
-
-
- Structure for database: Example.DBF
- Field Field Name Type Width Dec
- 1 Field1 Character 100
- 2 Field2 Character 70
- 3 Comments Memo 10
- ** Total ** 181
-
- These display commands
-
- ? SPACE( 1 ), Comments
- ? Field1, Comments
- ? Field2, Comments
- ? Field1, Field2, Comments
-
- will all produce different results.
-
- If the total length of the fields preceding the memo field is 29 or above,
- the first line of the memo field will wrap around. The entire memo field
- will then be displayed in double space. Futhermore, if the total length of
- the fields preceding the memo field is long enough to force the first line
- of the memo field to be displayed starting on the second line of the
- display, then the entire memo field will be displayed in triple space.
-
-
-
-
- 1.7 RECNO() to ROUND()
-
- >>> REPORT FORM with right margin=report width
-
- CREATEing a REPORT FORM with a right margin value equal to the report width
- (the page width minus the left margin) will display garbage to the screen
- or printer. This happens beacuse there is no space to print the report.
-
- There is a general misconception about the meaning of the right margin in
- the REPORT FORM. Some users have the impression that its value is the
- number of characters from the left margin, much the same way a typewriter
- works. The value actually refers the the number of characters the right
- margin is offset from page width.
-
- For example,
-
- page width |------------------------------------->|
- right margin |<----------|
-
-
- >>> REPORT FORM with stacked columns
-
- CREATEing a REPORT FORM field that consists of stacked columns built with
- the result of the STR() function and numeric fields may cause numbers to
- display incorrectly. Specifically, entering:
-
- STR( Field1, 5 ) + STR( Field2, 5 )
-
- in the field contents and specifying a column width of five will result in
- a correct display only when the fields have five-digit numbers stored in
- them. If the number has less than five digits, the display will be
- misplaced by the number of digits missing. The example above will produce:
-
- 11111
- 11111
-
- if the fields are full, but:
-
- 1111
- 1111
- 1111
-
- if there are only four digits in the fields.
-
-
-
-
- 1.8 SET <full-screen> to SORT
-
- >>>SET FILTER TO with GO BOTTOM
-
- If a SET FILTER TO condition is not satisfied by any records in the
- database file and a GO BOTTOM is issued, both the EOF() and BOF() will
- return a true (.T.). Removing the filter by issuing a SET FILTER TO does
- not reset EOF() or BOF(). The record pointer must be repositioned to reset
- EOF() and BOF(). SKIP or SKIP -1, however, will return a file boundry
- error message, because EOF() and BOF() are true. To move the record
- pointer appropriately issue a GO TOP and the BOF() and EOF() values will be
- reset to false (.F.).
-
-
-
- 1.9 STORE to ZAP
-
-
- 2 dBASE III Programming
-
-
- 3 dBASE III Frequently Asked Questions
-
- 3.1 Installation
-
-
- 3.2 Commands
-
-
- 3.3 New data types
-
-
- 3.4 Memory variables
-
-
- 3.5 Printing
-
-
- 3.6 Data transfer
-
-
- 4 dBASE III Reference
-
- 4.1 @...GET to Boolean Operators
-
-
- 4.2 CHR() to FILE() function
-
-
- 4.3 FIND to MODIFY STRUCTURE
-
- >>> Get Current Directory
-
- dBASE III has no facility to get the name of the current directory. To get
- it you must RUN the PC/MS-DOS command CD and import the results into dBASE
- III. The basic algorithm is as follows:
-
- 1. Create or have available a general-purpose database file called
- Util.DBF. Util.DBF has one field called Util_line which is
- character type and has a length of 80.
-
- 2. RUN the PC/MS-DOS command CD, piping the result into a text file
- entitled Util.TXT.
-
- 3. APPEND the text file Util.TXT into the database file, Util.DBF.
-
- 4. Assign to a memory variable the name of the current DOS directory
- from Util.DBF.
-
- The code that will execute this algorithm is as follows:
-
- SET SAFETY OFF
- RUN CD > Util.TXT
- USE Util
- ZAP
- APPEND FROM Util SDF
- currdir = TRIM( Util_line )
- SET SAFETY ON
- RETURN
-
-
- >>> Get Diskspace
-
- In the Developer's Release use the DISKSPACE() function to get the amount
- of space left on the currently logged drive. The DISKSPACE() will return
- the number of free bytes on the default drive as a numeric value.
-
- dBASE III versions 1.0 and 1.1 do not have a function to return the amount
- of space left on the default drive. So, to get the amount of diskspace in
- these versions, use the PC/MS-DOS utility CHKDSK and import the results
- into dBASE III. The basic algorithm is as follows:
-
- 1. Create or have available a general purpose database file called
- Util.DBF. Util.DBF has one field called Util_line which is
- character type and has a length of 80. This database file will be
- useful for any of these kinds of survey operations into PC/MS-DOS.
-
- 2. RUN CHKDSK including the designator of the drive for which you
- want the space statistic for, and pipe the result into a text file
- entitled Util.TXT. Piping is a PC/MS-DOS capability that allows
- the results of a program to be sent into a text file. It is very
- useful for passing parameters between programs when there is no
- formalized interface. The syntax is:
-
- <DOS commmand> > <result text file>
-
- ^_____ DOS piping symbol
-
- For more information on this capability, consult your PC/MS-DOS
- reference manual.
-
- 3. APPEND the text file, Util.TXT, into the database file, Util.DBF.
-
- 4. Assign to a memory variable the number of free bytes on the
- specified drive from Util.DBF. This operation requires that you
- GOTO the record that contains the free disk space information and
- then extract the number of bytes from the field using the SUBSTR()
- function.
-
- The following is a LIST of Util.DBF with the results of a CHKDSK
- report. When APPENDed into a database file, the first and last
- records are always blank. Records 2 through 6 contain statistics
- about the currently logged disk drive. Note that this is the
- currently logged DOS drive and not the DEFAULT drive SET in dBASE
- III. Records 8 and 9 contain statistics about the memory
- configuration of your computer. The number of bytes for each
- attribute of the drive and memory occupy positions 1 through 9 in
- the database field, Util_line.
-
- Record#
- 1
- 2 9965568 bytes total disk space
- 3 155648 bytes in 4 hidden files
- 4 90112 bytes in 22 directories
- 5 6000640 bytes in 397 user files
- 6 3719168 bytes available on disk
- 7
- 8 524288 bytes total memory
- 9 122480 bytes free
- 10
-
- The code that will get the the number of free bytes on the specified disk
- drive is as follows:
-
- SET SAFETY OFF
- RUN CHKDSK > Util.TXT
- USE Util
- ZAP
- APPEND FROM Util SDF
- GO 6
- diskspace = STR( SUBSTR( Util_line, 1, 9 ), 9 )
- USE
- SET SAFETY ON
- RETURN
-
-
- >>> Get Last Update and Time
-
- To get the date of last update for the currently SELECTed database file in
- the Developer's Release of dBASE III, use the LUPDATE() function.
- LUPDATE() returns the date of last update as a value of date type.
-
- dBASE III versions 1.0 and 1.1 currently do not have a function that
- returns the date of last update for the SELECTed database file. To get the
- date of last update in these versions of dBASE III, use the PC/MS-DOS
- command DIR, and import the results into dBASE III. The basic algorithm is
- as follows:
-
- 1. Create or have available a general purpose database file called
- Util.DBF. Util.DBF has one field called Util_line which is
- character type and has a length of 80.
-
- 2. RUN the PC/MS-DOS command DIR with the name of your database file,
- piping the result into a text file entitled Util.TXT.
-
- 3. APPEND the text file Util.TXT into the database file Util.DBF.
-
- 4. Assign to a memory variable the date of last update from Util.DBF
- for your database file.
-
- The code that will get the last update of the currently SELECTed database
- file is as follows:
-
-
- SET SAFETY OFF
- RUN DIR <Yourfile>.DBF > Util.TXT
- USE Util
- ZAP
- APPEND FROM Util SDF
- lupdate = SUBSTR( Util_line, 25, 8 )
- luptime = SUBSTR( Util_line, 34, 6 )
- USE
- SET SAFETY ON
- RETURN
-
-
- >>>Installation
-
- If the message "Insert System Disk #2 or press Ctrl-Break" appears when
- dBASE III is being loaded from a hard disk and is installed, the overlay
- file (DBASE.OVL) is corrupted. One possibility is that the copy on the
- hard disk is corrupted and can no longer be used. Another, and more
- likely, possibility is that the copy on System Disk #2 is bad, and you are
- now trying to run dBASE III after just having installed to the hard disk.
- If this occurs, contact the Ashton-Tate Customer Service Department.
-
-
- >>> MEMO fields
-
- (1) MEMO fields are used to contain up to 5,000 characters of text
- information that is to be associated with a database record. Information
- may be read into a MEMO field using Ctrl-K-R and written to text files
- using Ctrl-K-W. Information from MEMO fields can be displayed or printed
- by using LIST, DISPLAY, ?. The field must be specified with these
- commands. However, these commands cause the MEMO field to wrap at 50
- columns. The REPORT FORM may be used to output MEMO fields with line
- widths of more or less than 50 characters.
-
- (2) PACKing a database file with memo fields will not decrease the amount
- of disk space used by the .DBT file. The command file below demonstrates
- how to remove the deleted records and free the unused disk space.
-
- SET DELETED ON
- USE Filea
- COPY TO Temp
- CLOSE DATABASE
- ERASE Filea.dbf
- ERASE Filea.dbt
- RENAME Temp.dbf TO Filea.dbf
- RENAME Temp.dbt TO Filea.dbt
- SET DELETED OFF
-
-
-
-
- 4.4 Numeric fields to PARAMETERS
-
-
- 4.5 PRIVATE to PROCEDURE
-
-
- 4.6 PUBLIC to REPORT FORM
-
- >>> Ramdisk
-
- There are several options for users who wish to use a ramdisk in
- combination with dBASE III.
-
- 1. For faster operation of applications that utilize routines stored
- in the DBASE.OVL file, you may wish to put the .OVL in a ramdisk.
-
- (a) The minimum drive size will have to be in excess of 181,000
- bytes. The DBASE.OVL file for version 1.1 is 180,736 bytes.
- The total amount of RAM in your machine must be more than
- 440,000 bytes in order to do this. Additionally, if you are
- using a CONFIG.DB, it must be present on the drive where .OVL
- resides.
-
- (b) Boot dBASE III from the ramdisk by calling for the DBASE.COM
- from the drive on which it resides. For example: drive D: is
- the ramdisk and the DBASE.COM is on the C: drive.
-
- C> D:
- D> C:DBASE
-
-
-
- 2. It may be a very useful area for procedure or command files to be
- run from, increasing the speed of processing. Prior to entering
- dBASE III, copy the appropriate files to the ramdisk. Once in
- dBASE III, SET the DEFAULT TO the ramdisk drive and proceed.
-
- 3. It is also useful as a small work area to manipulate utility and
- temporary files. The useage tips on getting the current directory
- or diskspace are good examples of where a small ramdisk would be
- extremely useful and time efficient.
-
-
- >>> REPORT FORM
-
- The semicolon is not documented as functioning as a Carriage
- Return/Line-Feed in certain parts of REPORT FORMs.
-
-
- >>> REPORT FORM grouped by week
-
- If you have a date-oriented report and you need to have it grouped by week,
- the following discussion will assist you.
-
- The grouping of dates into weeks has two requirements. First, the database
- file you are reporting from must be INDEXed on the date field that is being
- grouped on. Second, as the group expression in your REPORT FORM, you must
- have an expression that returns as its value the first day of the week for
- each date field. The expression is as follows:
-
- Yourdate - ( DOW( Yourdate ) - 1 )
-
- When given any date value, this expression returns the date of the previous
- Sunday. It does this by subtracting from your date field the number of
- days that have passed since the last Sunday, the first day of the week in
- the dBASE III calendar. This value is obtained by subtracting 1 from the
- result of the DOW() function.
-
- If you wish to have the week you are grouping on start on a later day such
- as Monday, subtract more from the result of the DOW() function. For
- example, Monday would be DOW() - 2, Tuesday DOW() - 3, and so on.
-
-
-
-
- 4.7 RELEASE to SET PROCEDURE
-
-
- 4.8 SET RELATION to warnings
-
- >>>WAIT TO
-
- When a function key is SET to a literal character string, the WAIT command
- will not accept the assigned string, although ACCEPT TO will. Instead, the
- WAIT command will take the ASCII code respresentation of the function key
- itself. For example:
-
- SET FUNCTION 10 TO "A"
- WAIT TO var
-
- * ---Press F10.
- DISPLAY MEMORY
-
- VAR pub C "v"
- 1 variables defined, 3 bytes used
- 255 variables available, 5997 bytes available
-
- * ---Test to see what ASCII character F10 sent.
- ? ASC(var)
- 246 <------------------------- Code for F10
- ? ASC("A")
- 65 <-------------------------- Code for "A"
-
-
-
-
- 4.9 dBase III File Structure
-
-
- 4.10 dBASE III Memo File Structure
-
-
- 4.11 Installation and Configuration
-
-
- 5 dBASE III Sample Programs
-
-
- 6 dBASE III Technical Notes
-
- 6.1 Shifted Data Displays
-
- by OLiver Biggerstaff
-
- A database file may become corrupted for any number of reasons. Often the
- corruption may be the form of shifted data in full-screen edit screen
- displays. This is caused by an embedded null character in a record. A
- null character is represented as a 00 hex and is used by dBASE II and dBASE
- III as a string terminator. A string terminator is a character that can be
- thought of as a delimiter, much like double quotes surrounding a character
- string, or as a carriage return and linefeed at the end of a record.
-
- The dBASE APPEND, EDIT, BROWSE, and other full-screen commands work on the
- principle that the cursor's positioning on the screen depends on certain
- attributes, such as the length of a field and its current position. The
- appearence of shifted data is caused by the embedded string terminator
- forcing dBASE to stop the display of a field prematurely, placing the
- cursor at an incorrect location. If a null character is encountered before
- all the characters of a field have been displayed, dBASE will stop listing
- that particular field and will produce the shift effect by displaying the
- next field at an incorrect screen location.
-
- The data is actually not shifted physically in the database file. It is
- simple, therefore, to correct the database by replacing any null character
- with another character that does not force dBASE to display incorrectly. A
- good choice for this character is the ASCII character zero (0) or 30 hex.
- Replacing a null with this character is advantageous for two reasons:
-
- 1. Since dBASE can display it, you can locate the corrupted data.
-
- 2. A zero character will have the least disruptive effect on the
- contents of the database file. Additionally, logical fields that
- contain a zero character will be displayed as (.F.)
-
- Once all the null characters have been replaced, the BROWSE or EDIT
- commands can be used to retype the original data in place of the characters
- that replaced the nulls.
-
- The following is one of many methods that can be used to replace null
- characters with other characters. In this example we use the PC/MS-DOS
- utility DEBUG.COM, since most of you have this program on your supplemental
- PC/MS-DOS disk. These examples assume that the database file is 64K or
- less in size. Refer to the PC/MS-DOS manual for more information on DEBUG
- and how to use data segments if the database file is larger than 64K.
-
- The contents contained in the < and > symbols must be calculated by you,
- and entered without the symbols. For example, if the value of the CX
- register is 2C80H, then <CX+100H> is to be replaced with 2D80H. Be sure
- that before you attempt this procedure, you have made a backup of your
- database file.
-
- For dBASE II users on a 16-bit computer:
-
- C>DEBUG <database>.DBF ;Read database file into memory.
- -RCX ;Get the value in the CX
- ;register.
- -S 309 <CX+100H> 00 ;Search for nulls in the file.
- .
- . ;A list appears here of one or
- . ;more addresses containing a
- . ;null.
- -E <address> 30 ;Replace each individual address
- ;that contains a null with a
- ;zero.
- -W ;Save the modifications to disk.
- -Q ;Quit DEBUG.
-
- For dBASE III users:
-
- C>DEBUG <database>.DBF ;Read database file into memory.
- -RCX ;Get the value of the CX
- ;register.
- -S 100H 1121H 0DH ;Search for the end of the
- ;header for address containing
- ;0DH
- xxxx:yyyy ;for address containing 0D.
- ;Search for null characters.
-
- -S <yyyy+2> <CX+100H> 00
-
- .
- . ;List of addresses containing
- . ;nulls.
- .
- -E <address> 30H ;Replace each null with a zero.
- -W ;Save the modifications to
- ;disk.
- -Q ;Quit DEBUG.
-
- Unfortunately, this method of replacing null characters can be very tedious
- if many characters must be replaced. For those of you with a large amount
- of corrupted data, it is suggested that you use other well-known utility
- programs such as NIBBLER, NORTON UTILITIES, JAZ, or PATCH. These programs
- will allow you to look at very large database files directly from the hard
- disk. Some of these programs may also have a global search and replace
- option.
-
-
- 6.2 Swapping Printer Ports on the IBM PC
-
- by Robert Boies
-
- Last month in the dBASE II section of TechNotes we presented an assembler
- routine to swap printer ports on an IBM PC. This routine allows the user
- or programmer to toggle the LPT1 and LPT2 ports, making it easy to redirect
- output to several printers. This month we present essentially the same
- routine configured for dBASE III.
-
- If you have versions 1.0 or 1.1 of dBASE III, you will have to create an
- .COM file from this routine and at runtime use the RUN command to call it.
- For example from within dBASE III,
-
- * ---Print a first report to the default port, LPT1.
- REPORT FORM One TO PRINT
-
- * ---Print a second report to LPT2.
- RUN Portswap <---------------------------- Redirect to LPT2.
- REPORT FORM Two TO PRINT
- RUN Portswap <---------------------------- Restore LPT1.
-
-
- If you have the Developer's Release of dBASE III, you will be able to use
- the assembly language interface implemented in that version. For example:
-
- * ---LOAD into memory.
- LOAD Portswap
-
- * ---Print a first report to to the default port, LPT1.
- REPORT FORM One TO PRINT
-
- * ---Print a second report to LPT2.
- CALL Portswap <---------------------------- Redirect to LPT2.
- REPORT FORM Two TO PRINT
- CALL Portswap <---------------------------- Restore LPT1.
-
- * ---RELEASE the memory space.
- RELEASE MODULE Portswap
-
- The listing below, Portswap.ASM, uses the conditional directives discussed
- earlier to allow you to specify whether you wish to create a .BIN or .COM
- file.
-
- Assembly language source code:
-
- ; Program ..: Portswap.ASM
- ; Author ...: Robert Boies
- ; Date .....: September 1, 1985
- ; Note .....: Swaps the LPT1 and LPT2 ports in PC/MS-DOS 2.x
-
- .LFCOND ; List false conditionals.
- PAGE 60,132 ; Page length 60, line 132.
-
- COM EQU 0 ; Assemble as .BIN file
- D3 EQU 1 ; for Developer's Release.
- ;
- CODESEG SEGMENT BYTE PUBLIC 'CODE'
- ASSUME CS:CODESEG
- ;
- PORTSWAP PROC FAR
- ;
- IF COM
- ORG 0100H ; Originate at 0100H.
- ENDIF
- ;
- START: PUSH ES ; Save environment.
- PUSH AX
- PUSH BX
- PUSH DI
- ;
- MOV AX,40H ; System stores critical operating
- ; parameters at segment 40H (absolute
- ; address 400H).
- ; Load this segment address into AX.
- PUSH AX ; Put it on the stack.
- POP ES ; Pop the segment id from the
- ; stack into extra segment register.
- MOV DI,8H ; Port address of LPT1 and LPT2 are
- ; stored at offsets 8H and 0AH in
- ; this segment. Load DI with offset.
- MOV AX,ES:[DI] ; Put the port address of the
- ; first printer into the accumulator.
- INC DI ; Increment the destination index
- ; twice to point to port address
- INC DI ; of the second printer.
- MOV BX,ES:[DI] ; Put the port address of the
- ; second printer into BX.
- MOV ES:[DI],AX ; Poke the address of the first
- ; printer into the location of the
- ; second printer.
- DEC DI ; Decrement DI twice to point to the
- DEC DI ; location of the first printer
- ; port address.
- MOV ES:[DI],BX ; Poke the address of the second
- ; printer into the location of the
- ; first.
- POP DI ; Restore environment.
- POP BX
- POP AX
- POP ES
-
- IF COM
- INT 20H ; INT 20H if .COM file
- ELSE
- RET ; far return to dBASE III
- ENDIF
- ;
- PORTSWAP ENDP
- CODESEG ENDS
- END START
-
-
-
- 6.3 Interfacing Assembly Language Routines with dBASE
-
- by Ralph Davis
-
- Creating Assembler Programs with DEBUG
-
- DEBUG is the assembly language programmer's best friend. It is a powerful tool for
- exploring the computer's memory, testing assembly language programs, studying program
- listings, and creating new programs. Additionally, it can be used to rebuild corrupted
- data files, convert hidden files to accessible files, or simply analyze file structures.
- Our main interest in DEBUG here is to create assembly language routines for use with dBASE
- II and dBASE III.
-
- It is tempting to use DEBUG because of its interpreter-like qualities. You can quickly
- enter code and then see if it works.If it does, you call it <PROGRAM>.COM and write it to
- disk. If it doesn't, you trace through the old code, enter new code, and try again.
- Eventually, you come up with a program that works through trial-and-error. However, this
- can lead to sloppy programming habits and inefficient code, so it is important to bear in
- mind what you want a particular program to accomplish.
-
- DEBUG has some limitations. Most importantly, it only recognizes absolute addresses.
- When you write a program for submission to an assembler, you label the instructions and
- data you will need to refer to, then refer to them with the label. You don't need to know
- the actual addresses. DEBUG, on the other hand, obliges you to look through your program
- listing and find addresses whenever you refer to them. For instance, instead of entering
- JMP EXIT, you must enter JMP 02FC. Instead of CALL HEXPRINT, you use CALL 05AE. Instead
- of MOV BX, OFFSET DATA, you need MOV BX, 0105. If your routine is small, this does not
- present a problem. But as you add features and it becomes larger, this becomes a serious
- impediment. If you add or alter instructions, thereby changing an absolute address, you
- have to change every reference to it. And the only way to find the references is to page
- through the entire program, line by line. For this reason, DEBUG is best for creating
- short utility programs.
-
- Most often, programs created with DEBUG use BIOS or DOS interrupts to manipulate the
- hardware. Some typical functions that appear in this issue are setting the cursor (see
- the example on page 4-72C of the Developer's Release Reference Manual and the program
- listed in this issue), manipulating the shift keys, or swapping printer ports. Programs of
- this type should not contain any subroutines.
-
- DEBUG has another important limitation: it only understands hexadecimal numbers. There is
- simply nothing you can do to make it accept decimal numbers. This is not a problem when
- entering addresses or interrupt numbers, as most assembly language programmers think these
- values in hexadecimal anyway. But very few programmers think in hex when doing
- calculations. DEBUG is therefore not a good tool for doing number-crunching of even
- intermediate complexity. Although there are utilities available to assist in this
- process, such as Sidekick, this is still a major obstacle to doing extensive calculations
- within DEBUG.
-
- Another problem with DEBUG is that code produced with it can be extremely obscure. Trying
- to decipher the flow of a program where you have only absolute addresses and hexadecimal
- numbers to guide you can be very frustrating. In addition, DEBUG does not support
- comments. So when you read a DEBUG listing, you are, for all intents and purposes,
- reading "machine English." The machine expresses its own language in cryptic English-like
- symbols, making a few grudging concessions to your desire to understand it. All of this
- reinforces what we suggested earlier: keep DEBUG routines short.
-
- The program from the Developer's Release Reference Manual mentioned above is a good
- example of a program appropriate for DEBUG. The listing on page 4-72C is as follows:
-
- _PROG SEGMENT BYTE
- ASSUME CS:_PROG
- ;
- CURSOR PROC FAR ; Force a far return.
- ;
- MOV CX,[BX] ; Get two HEX digits.
- MOV AH,1 ; Set cursor type.
- INT 10H ; Video interrupt.
- RET ; Do a far return.
- ;
- CURSOR ENDP
- ;
- _PROG ENDS
- END
-
- This is a terse routine that converts the dBASE III cursor to a full-sized box when
- CHR(18) passed as a parameter to it. Notice one thing about this code: it has six lines
- of assembler directives (the first three and the last three), and only four lines of
- machine instructions. In a short program like this one, there is no advantage to
- assembling, linking, and converting it using MASM, LINK, and EXE2BIN. DEBUG is faster and
- easier.
-
- Here is a DEBUG session that enters this program as a .COM file.
-
- (The DEBUG commands are explained in Chapter 8 of the PC/MS-DOS manual. Page numbers
- which follow refer to it.)
-
- D>debug
-
- First give DEBUG the 'A' (assemble) command (page 8-15) and enter the program.
-
- -A
- 6257:0100 MOV CX,[BX]
- 6257:0102 MOV AH,1
- 6257:0104 INT 10
- 6257:0106 INT 20
- 6257:0108
-
-
- Notice that 'INT 20' is our last instruction, not 'RET' as the manual indicates. We will
- explain this shortly.
-
- The address following the last instruction is 108. Therefore, enter eight into CX using
- the 'R' (register) command [page 8-41]. This tells DEBUG the number of bytes to write to
- disk.
-
- -RCX
- CX 0000
- :8
-
- Name the program CURSOR.COM using the 'N' command [page 8-37], and write it to disk using
- 'W' [page 8-55].
-
- -NCURSOR.COM
- -W
- Writing 0008 bytes
-
- This is the basic procedure for creating a .COM file from DEBUG. CURSOR.COM will yield
- unpredictable results executed from PC/MS-DOS, since the registers are not preserved, and
- we have no way of knowing what is being passed in DS:BX. (When we tested it, the cursor
- simply vanished.) Nor, in its present form, will it work in dBASE III. It needs a couple
- of changes to make it work, but this point deserves some attention.
-
- PC/MS-DOS .COM files and dBASE LOAD modules require slightly different specifications. A
- .COM file must be ORGed (originated) at address 100H, and it must end with a command like
- INT 20H (terminate) or INT 27H (terminate and stay resident); a simple RET will not return
- correctly. dBASE III, on the other hand, requires LOAD modules to be ORGed at address 0
- and to return to dBASE III with a far return, RETF. If you load a conventional .COM file,
- ORGed at 100H and terminated with INT 20H, into dBASE III, and then call it, you will lock
- the system, even if it works from PC/MS-DOS. When DEBUG writes a program to disk, it
- writes a binary file -- that is, a file which contains nothing but the machine
- instructions you have given it. Therefore, we need not concern ourselves with ORGing
- programs correctly at this stage. We do have to terminate LOAD modules with RETF, however.
- Here is a DEBUG session that enters this program as a .BIN file which will execute from
- dBASE III.
-
- D>debug
-
- Type 'A' for assemble. Terminate with a RETF.
-
- -A
- 6346:0100 MOV CX,[BX]
- 6346:0102 MOV AH,1
- 6346:0104 INT 10
- 6346:0106 RETF
- 6346:0107
-
- Place the number 7 in the CX register to save 7 bytes to disk.
-
- -RCX
- CX 0000
- :7
-
- Name the file, and write it.
-
- -NCURSOR.BIN
- -W
- Writing 0007 bytes
-
- Quit DEBUG.
- -Q
-
- The page of the Developer's Release Manual referred to above gives the following example
- of how to use Cursor:
-
- LOAD Cursor
- STORE CHR(18) TO shape
- CALL Cursor WITH shape
-
- The commands to convert the cursor back to its normal format are:
-
- LOAD Cursor
- STORE CHR(12) + CHR(11) to shape
- CALL Cursor WITH shape
-
-
- .COM Files vs. .EXE Files
-
- When creating programs with a full-featured assembler, we have two options: .COM files and
- .EXE files. Each has advantages and disadvantages.
-
- .COM files are an inheritance from the world of 8-bit CP/M. They are your only option if
- you have a CP/M machine. .COM files must adhere to a strictly defined structure.
-
- 1. They must fit entirely within one segment. All segment registers must point to
- the same address, and cannot be changed during the execution of the program. This
- means that all of our main program, subroutines, and data must fit in 64K. A 64K
- .COM file is a very large program -- each line of code assembles to between 1 and
- 6 bytes, so a 64K .COM file could have as many as 30,000 lines of source code.
-
- 2. They must be ORGed at 100H. When PC/MS-DOS loads a .COM file, it jumps to CS:100H
- and begins executing.
-
- 3. They must return control to their calling routine with either INT 20H or INT 27H,
- or the equivalent INT 21H function calls, 4CH and 31H.
-
- .COM files load more quickly than .EXE files, since no addresses need to be calculated at
- load time.
-
- The assembly language programs that dBASE II and dBASE III can execute as subroutines
- (with the CALL command) are variations of the .COM file. We will discuss the specifics of
- their formats later.
-
- .EXE files are less limited structurally. The segment registers can be freely
- manipulated, and each one can point to an entirely different 64K segment. .EXE files can
- therefore be much larger than .COM files. .EXE files were designed to take better
- advantage of the actual architecture of 16-bit 8086-based microprocessors. Having data in
- one segment, code in another, and the stack in a third allows much greater utilization of
- the memory space available in today's machines. It also provides us the semblance of
-
-
- structured programming in assembly language. The SEGMENT, PROC, ENDS, and ENDP operators
- give a program listing a much more organized appearance than it has with JMP and DB
- statements interspersed throughout the code.
-
- .EXE files take longer to load than .COM files, as many of the absolute addresses are not
- computed until load time. They also take up more disk space than .COM files. However,
- since they use much more of the 8086 family's capabilities, they can be much more powerful
- programs. The commercial programs which were handed down from the CP/M world are all .COM
- files, whereas those which were created since the advent of 16-bit machines are mostly
- .EXE files.
-
- Having said this, we will leave .EXE files behind. You cannot LOAD .EXE files from dBASE
- II or dBASE III. You can execute them with QUIT TO in dBASE II or RUN(!) in dBASE III.
- If you want to pass parameters to and from .EXE files, you must pass them in text
- files(the SDF format is recommended).
-
-
- Adapting Assembly Language Programs to dBASE II or III
-
- As mentioned earlier, the format of a dBASE II or III assembly language subroutine most
- closely resembles that of a .COM file. Most importantly, it must reside in one segment.
- Since it is intended as a subroutine, not as a stand-alone program, it will differ
- somewhat from a standard .COM file.
-
- For one thing, a .COM file must be ORGed at 100H. However, ORGing a dBASE (II or III)
- subroutine at 100H will cause it to fail. A program intended for use in dBASE II must be
- ORGed high in the code segment -- the exact address depends on the version of dBASE II,
- the later the version, the higher the address. In version 2.43*, the ORG address should
- be above 61440 decimal. (See Robert Boies' article on swapping printer ports in the August
- issue of TechNotes for a good example of a dBASE II assembly language program.) A program
- intended for dBASE III must be ORGed at 0 (that is, it need not have an ORG statement).
- Secondly, .COM files return to their caller with interrupts (usually 20H or 27H), whereas
- dBASE II and dBASE III routines require RET (return) -- near for dBASE II, far for dBASE
- III.
-
- The procedure for converting assembly language source code into programs dBASE II or III
- can execute are as follows:
-
- 1. For dBASE II, you must assemble your program with an assembler that produces a
- file in Intel .HEX format. Intel's assemblers, ASM (for CP/M) and ASM86 (for
- CP/M-86), create such a file. For PC/MS-DOS, the Seattle Computer Products
- assembler generates a .HEX file. Refer to their manuals, as their assembly
- language syntax differs somewhat from Microsoft's and IBM's.
-
- 2. For dBASE III, use the IBM or Microsoft Macro-Assembler (MASM.EXE) to produce a
- .OBJ (object) file. Enter the command as follows:
-
- MASM <filename> <filename> <filename>;
-
- The third parameter will cause MASM to produce a listing file with a .LST
- extension, which is very useful for debugging.
-
- 3. Use the linker utility (LINK.EXE) that comes both with PC/MS-DOS and with the
- assembler. This will create an .EXE file. The command is:
-
- LINK <filename>
-
- Press Return three times in response to the prompts.
-
- 4. Use EXE2BIN.EXE to convert the program to .COM or .BIN format. If you are
- creating a .BIN file, you need only enter one parameter in the command line:
-
- EXE2BIN <filename>
-
- If you are creating a .COM file, you need to specify the full target filename:
-
- EXE2BIN <filename> <filename>.COM
-
-
- Using Conditional Assembler Directives
-
- Because the differences between .COM files and .BIN files are minor, it is possible to
- generate both using the same source code. The following program skeleton shows how to set
- this up. The EQU statements at the top inform the assembler whether we are assembling a
- program for PC/MS-DOS or dBASE III. In the present example, we have set COM equal to 0
- (meaning false) and D3 equal to 1 (non-zero, meaning true). We then use conditional
- directives to tell the assembler how we want the program created. Conditional directives
- are statements in your assembly program to direct the assembler to assemble a block of
- instructions based on a variable value. For example, IF COM (if COM is not zero), ORG the
- program at offset 100H. Then at the end of the program, IF COM, exit with INT 20H;
- otherwise, exit with a far RET.
-
- .LFCOND ; List false conditionals,
- PAGE 60,132 ; page length 60, line 132.
-
- COM EQU 0 ; Assemble program as .BIN
- D3 EQU 1 ; file for dBASE III.
-
- CODESEG SEGMENT BYTE PUBLIC 'CODE'
- ROUTINE PROC FAR
- ASSUME CS:CODESEG,DS:CODESEG
-
- IF COM
- ORG 100H
- ENDIF
-
- PUSH DS ; Make sure DS points to
- PUSH CS ; the current
- POP DS ; segment.
- .
- . (program goes here)
- .
- .
-
- POP DS ; Restore caller's DS.
- IF COM
- INT 20H ; INT 20H if .COM file.
- ELSE
- RET ; Far return if dBASE III
- ENDIF
-
- ROUTINE ENDP
- CODESEG ENDS
- END
-
- It is very important to load the DS register with the segment address contained in CS.
- PC/MS-DOS does this automatically for a .COM file, but dBASE III does not. Therefore, if
- your routine needs to access its own data, it will need to set DS correctly.
-
-
- Sample Program With Conditional Assembly
-
- Here is an program built on the skeletal structure which sets condensed print on an EPSON
- printer.
-
- ; Program ...: Printer.ASM
- ; Author ....: Ralph Davis
- ; Date ......: September 1, 1985
-
- TITLEPRINTER.ASM -- sets condensed print
-
- .LFCOND
- PAGE60,132
-
- COMEQU0
- D3EQU1
-
- CODESEGSEGMENTBYTE PUBLIC 'CODE'
- PRINTER PROCFAR
- ASSUME CS:CODESEG,DS:CODESEG
-
- IFCOM
- ORG100H
- ENDIF
-
- START:JMPSHORT ENTRY; Jump past data.
- CODESDB27,64,27,15; Printer control codes.
- CODELEN EQU$-CODES; Length of string.
- ENTRY: PUSHAX; Save registers.
- PUSHBX
- PUSHDS
- PUSHCS; Set up DS
- POPDS; with current segment.
- PUSHCX; Save CX
- PUSHDX; and DX.
- MOV BX,OFFSET CODES; Point BX to codes.
- MOVCX,CODELEN; Length of string.
- ; Controls the loop.
- GET_CODE:
- MOV DL,BYTE PTR [BX] ; Get code to send.
- MOV AH,5H ; PC/MS-DOS function 5H,
- INT 21H ; (send char to printer).
- INC BX ; Point to next code
- LOOP GET_CODE ; and print it.
-
- POPDX; Restore registers.
- POPCX
- POPDS
- POPBX
- POPAX
-
- IFCOM
- INT 20H ; INT 20H if .COM file.
- ELSE
- RET; Far return to dBASE III.
- ENDIF
-
- PRINTER ENDP
- CODESEG ENDS
- ENDSTART; End assembly.
-
-
- Assemble this program according to the instructions given earlier. To run it from dBASE
- II or dBASE III versions 1.0 and 1.1, assemble it as a .COM file, and enter the following
- commands:
-
- dBASE II:
-
- QUIT TO 'Printer'
-
- dBASE III:
-
- RUN Printer
-
- To run it from the Developer's Release of dBASE III, assemble it as a .BIN file, and use
- these commands:
-
- LOAD Printer
- CALL Printer
-
-
- 7 dBASE III Version 1.1 Change Summary